/*
 * File:   Memeory;anagement.c
 * Author: amalavassi
 *
 * Created on 11 de mayo de 2010, 05:31 PM
 */

#include <asm/bootparam.h>
#include <assert.h>
#include <elf.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>



#include "MemoryManagement.h"
#include "Utilities.h"



//convierte a una dirección lógica
void *        to_guest_logical_adress(unsigned long address){
    return guest_memory_base + address;
};

//convierte a una direccion física
unsigned long to_physical_adress(const void *address){
    return (address - guest_memory_base);
};

//Obtiene una cantidad de paginas de memoria
void          *get_pages(unsigned int num){
	void *addr = to_guest_logical_adress(guest_current_limit);
	guest_current_limit  += num * getpagesize();
	if (guest_current_limit > guest_max_memory)
		errx(1, "No hay suficiente memoria para satisfacer la petición");
	return addr;
};

//Obtiene paginas en blanco de memoria
void *map_zeroed_pages(unsigned int num)
{
	// Si no obtiene acceso mata el proceso de asignación
	int fd = do_or_burst("/dev/zero", O_RDONLY);
	void *addr;

	// Establece un esquema de copy on write


	addr = mmap(NULL, getpagesize() * num,
		    PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, 0);
	if (addr == MAP_FAILED)
		err(1, "Mmapping %u paginas de /dev/zero", num);

    guest_current_limit  += num * getpagesize();
	close(fd); // Aunque se cierre el file descriptor como se explico              de manera anterior el mapeo se mantiene
	return addr;
};

//Mapea un ejecutable a memoria
void  map_at(int fd, void *addr, unsigned long offset, unsigned long len){
    ssize_t r;

	//Los hypercalls son parcheados por el mismo kernel
	if (mmap(addr, len, PROT_READ|PROT_WRITE|PROT_EXEC,
		 MAP_FIXED|MAP_PRIVATE, fd, offset) != MAP_FAILED)
		return;

	//Avanza hasta el desplazamiento mientras lee
	r = pread(fd, addr, len, offset);
	if (r != len)
		err(1, "El Offset dio %lu len %lu dio %zi", offset, len, r);

};

//Mapeamos un ejecutable de Tipo ELF a memoria
unsigned long mapear_elf(int elf_fd, const Elf32_Ehdr *ehdr){
    Elf32_Phdr phdr[ehdr->e_phnum];
	unsigned int i;

	//Se verifica la integridad del archivo, si existe un error se elimina la ejecucción
	if (ehdr->e_type != ET_EXEC
	    || ehdr->e_machine != EM_386
	    || ehdr->e_phentsize != sizeof(Elf32_Phdr)
	    || ehdr->e_phnum < 1 || ehdr->e_phnum > 65536U/sizeof(Elf32_Phdr))
		errx(1, "Malformed elf header");

	//Leemos todos los headers
	if (lseek(elf_fd, ehdr->e_phoff, SEEK_SET) < 0)
		err(1, "Seeking to program headers");
	if (read(elf_fd, phdr, sizeof(phdr)) != sizeof(phdr))
		err(1, "Reading program headers");

	// Revisa todos los headers, el de lectura y escritura
	for (i = 0; i < ehdr->e_phnum; i++) {
		/*Si el segmento no se puede cargar se ignota*/
		if (phdr[i].p_type != PT_LOAD)
			continue;

		// Si se puede carghar debe mapearse el segmento del ELF a memoria
		map_at(elf_fd, to_guest_logical_adress(phdr[i].p_paddr),
		       phdr[i].p_offset, phdr[i].p_filesz);
	}

	/* TCargamos el punto de entrada, es un atribtuo del .ELF. */
	return ehdr->e_entry;
};

//Carga una imagen BZ para que se ejecue automaticamente
unsigned long cargar_bzimage(int fd){
        struct boot_params boot;
	int r;
	//Dirección predeterminada de carga de un BzImage
	void *p = to_guest_logical_adress(0x100000);

	//Lee el header
	lseek(fd, 0, SEEK_SET);
	read(fd, &boot, sizeof(boot));

	// Debe contener un Hdr5 para que sea un BZ image
	if (memcmp(&boot.hdr.header, "HdrS", 4) != 0)
		errx(1, "Esto no es un bzImage");
	lseek(fd, (boot.hdr.setup_sects+1) * 512, SEEK_SET);

	//Lee fragmentos grandes de memoria
	while ((r = read(fd, p, 65536)) > 0)
		p += r;

	//Retorna la direciń de inicio
	return boot.hdr.code32_start;
};

//Se encarga de cargar el kernel
unsigned long load_kernel(int fd){

    Elf32_Ehdr hdr;
    //Se lee el header
    if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr))
		err(1, "Reading kernel");

    //El fd apunta a un .elf o a un bzimage
    //Un elf contiene un identificador en el header ("\177ELF")
    if (memcmp(hdr.e_ident, ELFMAG, SELFMAG) == 0)
        return mapear_elf(fd, &hdr);

    //Es un BzImage
    return cargar_bzimage(fd);
};

//Alinea las paginas
inline unsigned long page_align(unsigned long addr)
{
	int size = getpagesize()-1;
	return ((addr + size) & ~(size));
}

//Carga un disco de ram para que el kernel puede arrancar y cargar los componentes que necesita
unsigned long load_initrd(const char *name, unsigned long mem)
{
	int ifd;
	struct stat st;
	unsigned long len;

	ifd = do_or_burst(name, O_RDONLY);

	/* TAMAÑO DEL aRCHIVO DE ram. */
	if (fstat(ifd, &st) < 0)
		err(1, "fstat() de initrd '%s'", name);

	//Cargamos el disco y lo alineamos en el tope de la memoria
	len = page_align(st.st_size);
	map_at(ifd, to_guest_logical_adress(mem - len), 0, st.st_size);
	close(ifd);

    //Retorna el tamaño del archivo del disco de RAM
	return len;
}


